﻿using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.ReSharper.Daemon;
using JetBrains.ReSharper.Daemon.Stages;
using JetBrains.ReSharper.Daemon.Xml.Highlightings;
using JetBrains.ReSharper.Psi.Tree;
using JetBrains.ReSharper.Psi.Xml;
using JetBrains.ReSharper.Psi.Xml.Tree;
using SPCAFContrib.Entities.Consts;
using SPCAFContrib.ReSharper.Common;
using SPCAFContrib.ReSharper.Common.Attributes;
using SPCAFContrib.ReSharper.Common.Psi.XmlCache;
using SPCAFContrib.ReSharper.Common.Extensions;
using SPCAFContrib.ReSharper.Common.XmlAnalysis;
using SPCAFContrib.ReSharper.Inspection.Xml;
using SPCAFContrib.ReSharper.Consts;

[assembly: RegisterConfigurableSeverity(MixedIDInFieldNameHighlighting.CheckId,
  null,
  Consts.BEST_PRACTICE_GROUP,
  MixedIDInFieldNameHighlighting.CheckId + ": " + MixedIDInFieldNameHighlighting.Message,
  "It might be suggested to avoid mixing up \"ID\" and \"Id\" while crafting field.",
  Severity.SUGGESTION,
  false, Internal = false)]

namespace SPCAFContrib.ReSharper.Inspection.Xml
{
    [Applicability(
        IDEProjectType.SP2010FarmSolution |
        IDEProjectType.SPSandbox |
        IDEProjectType.SP2013FarmSolution)]
    public class MixedIDInFieldName : XmlTagProblemAnalyzer
    {
        public override void Run(IXmlTag element, IHighlightingConsumer consumer)
        {
            if (element.GetProject().IsApplicableFor(this))
            {
                if (IsInvalid(element))
                {
                    IXmlAttribute attribute = element.GetAttribute("Name");
                    MixedIDInFieldNameHighlighting errorHighlighting =
                        new MixedIDInFieldNameHighlighting(attribute);
                    consumer.ConsumeHighlighting(attribute.GetDocumentRange(), errorHighlighting);
                }
            }
        }

        public static bool IsInvalid(IXmlTag element)
        {
            bool result = false;

            if (element.IsFieldDefinition() && element.AttributeExists("ID") &&
                (AttributeHasMixedIds(element, "Name") || AttributeHasMixedIds(element, "StaticName")))
            {
                var id = element.GetAttribute("ID").UnquotedValue;
                if (!String.IsNullOrEmpty(id))
                {
                    IEnumerable<FieldXmlEntity> fields =
                        FieldCache.GetInstance(element.GetSolution())
                            .Items.Where(f => CheckIDId(f.Name) || CheckIDId(f.StaticName));
                    result = fields.Any(f => f.Id != id);
                }
            }

            return result;
        }

        private static bool AttributeHasMixedIds(IXmlTag element, string attName)
        {
            bool result = false;

            if (element.AttributeExists(attName))
            {
                var attribute = element.GetAttribute(attName);
                if (!String.IsNullOrEmpty(attribute.UnquotedValue))
                {
                    result = CheckIDId(attribute.UnquotedValue);
                }
            }

            return result;
        }

        private static bool CheckIDId(string p)
        {
            return !String.IsNullOrEmpty(p) && p.EndsWith("ID") || p.EndsWith("Id");
        }
    }

    [ConfigurableSeverityHighlighting(CheckId, XmlLanguage.Name, OverlapResolve = OverlapResolveKind.NONE, ShowToolTipInStatusBar = true)]
    public class MixedIDInFieldNameHighlighting : XmlErrorHighlighting
    {
        public const string CheckId = CheckIDs.Rules.FieldTemplate.MixedIDInFieldName;
        public const string Message = "Avoid mixed \"ID\" and \"Id\" in static/internal field names";

        public IXmlAttribute Element { get; private set; }

        public MixedIDInFieldNameHighlighting(IXmlAttribute element) :
            base(String.Format("{0}: {1}", CheckId, Message))
        {
            Element = element;
        }

        public override bool IsValid()
        {
            return this.Element.IsValid();
        }
    }
}
